package server.io.client;

import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;

import server.io.code.EnDeCode_WSP;
import server.io.game.fact.hall.Hall_Base;
import server.io.game.fact.hall.Hall_Normal;
import server.io.game.fact.player.Player_Base;
import server.io.game.fact.room.Room_Base;
import server.io.game.fact.user.User_Base;
import server.io.game.fact.user.User_Normal;
import server.io.pro.bean.Pro_AllotCardHand;
import server.io.pro.bean.Pro_AllotHero;
import server.io.pro.bean.Pro_ChooseHero;
import server.io.pro.bean.Pro_JoinHall;
import server.io.pro.bean.Pro_JoinRoom;
import server.io.pro.bean.Pro_Login;
import server.io.pro.bean.Pro_StartGame;
import server.io.pro.ctrl.Pro_Adapter_Recv;
import server.io.pro.ctrl.Pro_Adapter_Send;

import com.google.gson.reflect.TypeToken;

public class Recv_Module extends Thread
{
	// 静态的监听read事件的selector.
	private Selector hallSelector;
	// 是否开始监听read.
	private boolean IsThreadShutDown = false;
	// 给此监听线程加入新channel时候,用于判断
	private boolean isJoiningNew = false;
	// 是否让监听器监听,如果为false则此线程将会睡眠并且被回收
	private boolean isListen = true;
	// 大厅的id,标识此大厅
	private int hallId = 0;
	// 存住select的返回值
	private int k = 0;
	// 读取信息的缓冲区
	// private ByteBuffer r_buff = ByteBuffer.allocate(1024);
	// 存放read()方法返回的int
	private int lengthOfRead = 0;
	
	private int idid = 1302;

	private ClientInfo global_Client_Info;

	private static ByteBuffer sendbuffer = ByteBuffer.allocate(8192);

	// 定义实现编码、解码的字符集对象
	private Charset charset = Charset.forName("UTF-8");

	// /////////////////////////////////////////
	// /////////////////////////////////////////
	// /////////////////////////////////////////
	// /////////////////////////////////////////
	// /////////////////////////////////////////
	// /////////////////////////////////////////
	// fact

	private Hall_Normal hall = null;

	// /////////////////////////////////////////
	// /////////////////////////////////////////
	// /////////////////////////////////////////
	// /////////////////////////////////////////
	// /////////////////////////////////////////
	// /////////////////////////////////////////

	public Recv_Module()
	{
		// TODO Auto-generated constructor stub
		init();
		this.hall = new Hall_Normal("small");
		this.hall.init();
	}

	@Override
	public void run()
	{
		System.out.println("Recv_Module Start...!");
		// 一开始初始化大厅监听线程时,先让其睡眠
		if (hallId == 0)
		{
			try
			{
				synchronized (this)
				{
					wait();
				}
			}
			catch (InterruptedException e1)
			{
				e1.printStackTrace();
			}
		}

		// 监听器主体
		while (!IsThreadShutDown)
		{
			while (isListen)
			{
				try
				{
					System.out.println("Waiting.. IO Data.. \n");
					k = hallSelector.select();
					if (k == 0)
					{
						// 此处给此监听器加入绑定新channel
						if (isJoiningNew)
						{
							System.out.println("One New Recv Listen..1");
							bindingNewClient();
						}
						continue;
					}

					System.out.println("一次循环   "
							+ hallSelector.selectedKeys().size() + "  "
							+ hallSelector.keys().size());

					Iterator<SelectionKey> it = hallSelector.selectedKeys()
							.iterator();

					while (it.hasNext())
					{

						SelectionKey key = it.next();

						it.remove();

						if (key.isReadable())
						{
							handleInput(key);
						}
					}

					// 此处给此监听器加入绑定新channel,与上面的判断双重保证此监听器能绑定到新client
					if (isJoiningNew)
					{
						System.out.println("One New Recv Listen..2");
						bindingNewClient();
					}
				}
				catch (Exception e)
				{
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 处理客户端的发入. 由管理类解析，次监听类本身判断并处理
	 * 
	 * @param argKey
	 * @throws IOException
	 */
	private void handleInput(SelectionKey argKey) throws IOException
	{
		// 由key获取指定socket channel的引用
		SocketChannel one_Channel = (SocketChannel) argKey.channel();
		ByteBuffer r_buff = ByteBuffer.allocate(1024);
		// 重置缓冲区
		r_buff.clear();

		try
		{
			lengthOfRead = one_Channel.read(r_buff);

			// if语句
			if (lengthOfRead > 0)
			{
				// r_buff.flip();
				// String content = "";
				// CharBuffer c = charset.decode(r_buff);
				// content = c.toString();

				r_buff.flip();

				byte[] bytes = new byte[r_buff.remaining()];
				r_buff.get(bytes);

				String content = EnDeCode_WSP.WSP13Decode(bytes);

				System.out.println("server revc: \n" + content);

				// 协议适配器
				Pro_Adapter_Recv _proAdp_Recv = new Pro_Adapter_Recv();
				Pro_Adapter_Send _proAdp_Send = new Pro_Adapter_Send();
				int _proId = _proAdp_Recv.parseHead(content);
				// 要发送的数据
				String _sendData_str = null;
				byte[] _sendData_byte = null;
				Type _beanType = null;
				// // 临时
				Room_Base _room = null;
				Player_Base _player = null;
				switch (_proId)
				{
				case 11:
					HandShake _handShake = new HandShake();
					_sendData_byte = _handShake.createProtocal(content);
					break;
				// 登录
				case 1001:
					Pro_Login _login = new Pro_Login();
					_login._recv = _proAdp_Recv.jsonToObject(content, Pro_Login.Recv.class);
					// 登陆成功
					User_Normal _user = new User_Normal(++idid, _login._recv.username);
					putAttach(argKey, "user", _user);
					// 协议是一个对象
					_login._send = _login.new Send("ok", _user.user_name, _user);
					// 把这个协议对象转成string
					_sendData_byte = _proAdp_Send.encapProtocol(_login._send);
					break;
				// 进入房间
				case 1053:
					_beanType = new TypeToken<Pro_JoinRoom.Recv>()
					{
					}.getType();
					Pro_JoinRoom _joinRoom = new Pro_JoinRoom();
					_joinRoom._recv = (Pro_JoinRoom.Recv) _proAdp_Recv
							.parseProtocol(content, _beanType);

					_room = ((Hall_Base) getAttach(argKey, "hall")).joinRoom(
							argKey, _joinRoom._recv.room_number);
					// 协议是一个对象
					_joinRoom._send = _joinRoom.new Send(_room);
					// 把这个协议对象转成string
					_sendData_byte = _proAdp_Send
							.encapProtocol(_joinRoom._send);
					break;
				// 开始游戏
				case 1055:
					_beanType = new TypeToken<Pro_StartGame.Recv>()
					{
					}.getType();
					Pro_StartGame _startGame = new Pro_StartGame();
					_startGame._recv = (Pro_StartGame.Recv) _proAdp_Recv
							.parseProtocol(content, _beanType);

					_room = (Room_Base) getAttach(argKey, "room");
					_room.startGame();

					// 协议是一个对象
					_startGame._send = _startGame.new Send(_room.game);
					// 把这个协议对象转成string
					_sendData_byte = _proAdp_Send
							.encapProtocol(_startGame._send);
					break;
				// 分配可选武将
				case 1059:

					_beanType = new TypeToken<Pro_AllotHero.Recv>()
					{
					}.getType();

					Pro_AllotHero _allotHero = new Pro_AllotHero();

					_allotHero._recv = (Pro_AllotHero.Recv) _proAdp_Recv
							.parseProtocol(content, _beanType);

					_room = (Room_Base) this.getAttach(argKey, "room");
					_player = _room.game.allotHero(argKey);

					// 协议是一个对象
					_allotHero._send = _allotHero.new Send(_player);

					// 把这个协议对象转成string
					_sendData_byte = _proAdp_Send
							.encapProtocol(_allotHero._send);
					// 再转成封装成websocket协议的byte[]
					break;
				// 选择武将
				case 1061:
					_beanType = new TypeToken<Pro_ChooseHero.Recv>()
					{
					}.getType();
					Pro_ChooseHero _chooseHero = new Pro_ChooseHero();
					_chooseHero._recv = (Pro_ChooseHero.Recv) _proAdp_Recv
							.parseProtocol(content, _beanType);
					_room = (Room_Base) getAttach(argKey, "room");
					_room.game
							.chooseHero(argKey, _chooseHero._recv.hero_factid);
					// 协议是一个对象
					_chooseHero._send = _chooseHero.new Send(_room.game.players);
					// 把这个协议对象转成string
					_sendData_byte = _proAdp_Send
							.encapProtocol(_chooseHero._send);
					break;
				// 分配手牌
				case 1063:

					_beanType = new TypeToken<Pro_AllotCardHand.Recv>()
					{
					}.getType();

					Pro_AllotCardHand _allotHandCard = new Pro_AllotCardHand();

					_allotHandCard._recv = (Pro_AllotCardHand.Recv) _proAdp_Recv
							.parseProtocol(content, _beanType);

					_room = (Room_Base) getAttach(argKey, "room");
					_player = _room.game.allotCardHand(argKey);

					// 协议是一个对象
					_allotHandCard._send = _allotHandCard.new Send(
							_player.card_hand);

					// 把这个协议对象转成string
					_sendData_byte = _proAdp_Send
							.encapProtocol(_allotHandCard._send);
					// 再转成封装成websocket协议的byte[]
					break;
				default:
					break;
				}

				System.out.println("server send:\n"
						+ new String(_sendData_byte));
				sendbuffer.clear();
				sendbuffer.put(_sendData_byte);
				sendbuffer.flip();
				one_Channel.write(sendbuffer);
				System.out.println("Send One..!");

			} // 若断线,cancel掉此通道
			else if (lengthOfRead == -1)
			{
				System.out.println("!!!!!!! Close One Connect!(in length=-1)");
				// 置null user
				this.putAttach(argKey, "user", null);
				
				User_Normal _user = (User_Normal) getAttach(argKey, "user");
				
				// 关闭此channel
				one_Channel.close();
				// 删掉此key
				argKey.cancel();
			}
		}
		catch (Exception e)
		{
			System.out.println("!!!!!!! Close One Connect!(in catch)");
			// 关闭此channel
			one_Channel.close();
			// 删掉此key
			argKey.cancel();
			e.printStackTrace();
		}
	}

	private void init()
	{

		try
		{
			hallSelector = Selector.open();
		}
		catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 进入大厅的同步性问题,保证一次一个.做一个lock,等待模式. set进channel的同时把isJoiningNew赋值为true
	 * 
	 * @param argUser
	 * @param argSign
	 */
	public void setUserInHall(final ClientInfo argUser, int argSign)
	{
		global_Client_Info = argUser;
		isJoiningNew = true;
		hallSelector.wakeup();
	}

	// 为大厅赋值自身id后，唤醒此大厅线程
	public synchronized void setHallId(int argHallId)
	{
		this.hallId = argHallId;
		this.notify();
	}

	private void bindingNewClient()
	{
		// 加入新玩家进入大厅,邦定selector
		// 新玩家加入时候触发此方法,邦定成功并且加入队列成功才继续
		if (handleNewJoining(global_Client_Info.channel, hallSelector,
				global_Client_Info))
		{
			isJoiningNew = false;
		}
		else
		{
			isJoiningNew = false;
		}
	}

	public void sleepThisListener()
	{
		isListen = false;
		try
		{
			hallSelector.close();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
	}

	public boolean handleNewJoining(SocketChannel argChannel,
			Selector argSelect, ClientInfo argUser)
	{
		try
		{

			argChannel.configureBlocking(false);
			argChannel.register(argSelect, SelectionKey.OP_READ, argUser);
			return true;
		}
		catch (IOException e)
		{
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 取出附带的内容
	 * 
	 * @param selectKey
	 * @param key
	 * @return
	 */
	public Object getAttach(SelectionKey selectKey, String key)
	{
		Object _result = null;
		ClientInfo _attach = (ClientInfo) selectKey.attachment();
		_result = _attach.map.get(key);
		return _result;
	}

	/**
	 * 更新附带的内容
	 * 
	 * @param selectKey
	 * @param key
	 * @return
	 */
	public void putAttach(SelectionKey selectKey, String key, Object value)
	{
		ClientInfo _attach = (ClientInfo) selectKey.attachment();
		_attach.map.put(key, value);
	}
}